#include <QtOpenGL>
#include "QyGLScene.h"

using namespace QyGL;

/**************************************************************************************************/

class DefVals {
public:
    explicit DefVals( qreal cursorZ = 0 ) {

        QyMath::RealVec points [3] = {
            { 1.0, 0.0, cursorZ },
            { 0.0, 1.0, cursorZ },
            { 1.0, 1.0, cursorZ }
        };

        QyMath::generatePlane( cursorPlane, points );

    }

    qreal cursorPlane[4];

};

/**************************************************************************************************/

Scene::Scene( const ViewSettings & viewSettings ) {

    p_viewSettings = viewSettings;
    p_camOffset.p[2] = -50;
    setCursorPlaneZ(0);
    p_selectBuffer = 0;
    p_initialized = false;
    timerId = ( p_viewSettings.updateRate > 0 ) ? startTimer(p_viewSettings.updateRate) : 0;
    update();

}

Scene::~Scene() {

    if (p_selectBuffer)
        delete [] p_selectBuffer;

}

void Scene::setCursorPlaneZ( qreal z ) {

    p_cursorZ = z;

    QyMath::RealVec points [3] = {
        { 1.0, 0.0, p_cursorZ },
        { 0.0, 1.0, p_cursorZ },
        { 1.0, 1.0, p_cursorZ }
    };

    QyMath::generatePlane( p_cursorPlane, points );

}

void Scene::beginSelect( int x, int y, quint32 selectBufferSize ) {

    if (!selectBufferSize)
        return;

    if (p_selectBuffer)
        delete [] p_selectBuffer;

    p_selectBuffer = new quint32[selectBufferSize];
    qint32 viewport[4];
    qreal vx, vy;

    glSelectBuffer( selectBufferSize, p_selectBuffer );
    glGetIntegerv( GL_VIEWPORT, viewport );
    glRenderMode(GL_SELECT);
    glInitNames();
    glMatrixMode(GL_PROJECTION);

    vx = x;
    vy = viewport[3] - y - 1;

    glPushMatrix();

    glLoadIdentity();
    gluPickMatrix( vx, vy, 5, 5, viewport );
    resize( qreal( width() ), qreal( height() ) );
    glPushMatrix();
    setupCam();

}

quint32 Scene::endSelect() {

    glPopMatrix();
    glPopMatrix();
    glFlush();

    quint32 hits = glRenderMode(GL_RENDER);

    resize( qreal( width() ), qreal( height() ) );

    return hits;
}

void Scene::setupCam() {

    glTranslated( p_camOffset.p[0], p_camOffset.p[1], p_camOffset.p[2] );
    glRotated( p_camDir.p[0], 1.0, 0.0, 0.0 );
    glRotated( p_camDir.p[1], 0.0, 1.0, 0.0 );
    glRotated( p_camDir.p[2], 0.0, 0.0, 1.0 );
    glTranslated( p_camPos.p[0], p_camPos.p[1], p_camPos.p[2] );

}

/*static */QyMath::Vector Scene::mapToGL( const QPointF & pos ) {

    static DefVals defVals(0);

    QyMath::Beam beam = QyMath::generateBeam( int( pos.x() ), int( pos.y() ) );
    QyMath::Vector glPos = QyMath::getPointOfPlane( beam, defVals.cursorPlane );
    glPos[0] = -glPos[0];
    glPos[1] = -glPos[1];
    glPos[2] = 0;

    return glPos;
}

/*static */QPointF Scene::mapFromGL( const QyMath::Vector & pos ) {

    qint32 viewport[4];
    qreal projection[16],
          modelview[16],
          x, y, z;

    glGetIntegerv( GL_VIEWPORT, viewport );
    glGetDoublev( GL_PROJECTION_MATRIX, projection );
    glGetDoublev( GL_MODELVIEW_MATRIX, modelview );

    gluProject( pos.p[0], -pos.p[1], pos.p[2], modelview, projection, viewport, &x, &y, &z );

    return QPointF( x, y );
}

void Scene::initialize() {
}

void Scene::paint() {
}

void Scene::resize( qreal width, qreal height ) {

    gluPerspective(
        p_viewSettings.projection.fov,
        p_viewSettings.projection.adjustAspect ? width / height : p_viewSettings.projection.aspect,
        p_viewSettings.projection.zNear,
        p_viewSettings.projection.zFar
    );

}

void Scene::drawBackground( QPainter * painter, const QRectF & rect ) {

    if (!p_initialized) {

        initialize();
        p_initialized = true;

    }

    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();

    glMatrixMode(GL_MODELVIEW);
    glPushMatrix();
    glLoadIdentity();

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    resize( qreal( width() ), qreal( height() ) );

    glMatrixMode(GL_MODELVIEW);

    glPushMatrix();
    setupCam();

//     glPushMatrix();
    QyMath::Beam beam = QyMath::generateBeam( p_pos.x(), p_pos.y() );
    p_glCursor = QyMath::getPointOfPlane( beam, p_cursorPlane );
    p_glCursor[0] = -p_glCursor[0];
    p_glCursor[1] = -p_glCursor[1];
    p_glCursor[2] = p_cursorZ;

    paint();
    glPopMatrix();

    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();

    glMatrixMode(GL_PROJECTION);
    glPopMatrix();

}

void Scene::mouseMoveEvent( QGraphicsSceneMouseEvent * event ) {

    p_pos = event->scenePos().toPoint();

//     QyDbgLocation();
//     QyDbgValue(p_pos);

    QGraphicsScene::mouseMoveEvent(event);

}

void Scene::timerEvent( QTimerEvent * event ) {

    if ( timerId == event->timerId() )
        update();
    else
        QGraphicsScene::timerEvent(event);

}
